#include <slothttpd/data_buffer.hpp>
#include <slothttpd/types.hpp>

#include <cstdlib>
#include <cstddef>
#include <string>
#include <sstream>
#include <algorithm>

using namespace slothttpd;

//g++ -I./ ./test/receive_buffer_list_test.cpp --std=c++0x -lboost_system -pthread -g ./slothttpd/receive_buffer.cpp && ./a.out

void test1()
{
    receive_buffer_list list(10, 10, false);
    
    std::string src;
    
    std::srand(0);
    
    for (int i = 0; i < 100; i++) {
        src.push_back('0' + std::abs(std::rand()) % 10);
    }
    
    std::string out;
    auto it = src.begin();
    while (it != src.end()) {
        if (list.get_free_space().empty()) {
            std::cout << out << std::endl;
            throw std::logic_error("out of free space");
        }
        
        char_range_t free = list.get_free_space();
        
        for (std::size_t i = 0; i < std::size_t(free.size()); i++) {
            free[i] = *it;
            ++it;
        }
        
        list.provide(free.size());
        
        char_range_t data = list.get_data();
        
        for (char c : data) {
            out.push_back(c);
        }
        
        list.consume(data.size());
    }
    
    
    if (src != out) {
        std::cout << src << '\n';
        std::cout << out << std::endl;
        
        throw std::logic_error("src != out");
    }
}

void test2()
{
    receive_buffer_list list(10, 10, false);
    
    std::string src;
    
    std::srand(0);
    
    for (int i = 0; i < 100; i++) {
        src.push_back('0' + std::abs(std::rand()) % 10);
    }
    
    for (auto it = src.begin(); it != src.end(); ) {
        char_range_t free = list.get_free_space();
        
        if (free.empty()) {
            throw std::logic_error("out of free space");
        }
        
        for (std::size_t i = 0; i < std::size_t(free.size()); i++) {
            free[i] = *it;
            ++it;
            
            if (it == src.end()) {
                break;
            }
        }
        
        list.provide(free.size());
    }
    
    
    std::string out;
    
    while (!list.get_data().empty()) {
        char_range_t data = list.get_data();
        
        for (char c : data) {
            out.push_back(c);
            list.consume(1);
        }
    }
    
    if (src != out) {
        std::cout << src << '\n';
        std::cout << out << std::endl;
        
        throw std::logic_error("src != out");
    }
}

void test3()
{
    receive_buffer_list list(10, 10, true);
    
    std::string src;
    
    std::srand(0);
    
    for (int i = 0; i < 100; i++) {
        src.push_back('0' + std::abs(std::rand()) % 10);
    }
    
    auto it = src.begin();
    std::size_t left = 50;
    while (it != src.begin() + 50 && left > 0) {
        auto r = list.get_free_space();
        
        std::size_t n = std::min(left, std::size_t(r.size()));
        
        std::copy(it, it + n, r.begin());
        it += n;
        list.provide(n);
        left -= n;
    }
    
    if (left > 0) {
        throw std::logic_error("not enaugh space!");
    }
    /*
    {
        std::string out;
        while (!list.get_data().empty()) {
            char_range_t data = list.get_data();
            
            for (char c : data) {
                out.push_back(c);
                list.consume(1);
            }
        }
        
        std::cout << out << '\n';
    }
    */
    list.consume(50);
    
    list.switch_dont_save();
    
    left = 50;
    while (it != src.end() && left > 0) {
        auto r = list.get_free_space();
        
        std::size_t n = std::min(left, std::size_t(r.size()));
        
        std::copy(it, it + n, r.begin());
        it += n;
        list.provide(n);
        left -= n;
    }
    
    std::string out;
    
    for (char_range_t data = list.get_data(); !data.empty(); data = data = list.get_data()) {
        for (char c : data) {
            out.push_back(c);
        }
        
        list.consume(data.size());
    }
    
    src.erase(src.begin(), src.begin() + 50);
    if (src != out) {
        std::cout << src << '\n';
        std::cout << out << std::endl;
        
        throw std::logic_error("src != out");
    }
}

int main()
{
    test1();
    test2();
    test3();
}
